import 'package:flutter/material.dart';

class AnimationApp extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return MaterialApp(
      home: Scaffold(
//        body: ScaleAnimationRoute(),
        body: ScaleAnimationRoute1(),
      ),
    );
  }
}

class ScaleAnimationRoute extends StatefulWidget{
  @override
  _ScaleAnimationRouteState createState() => new _ScaleAnimationRouteState();
}

//需要继承TickerProvider，如果有多个AnimationController，则应该使用TickerProviderStateMixin。
class _ScaleAnimationRouteState extends State<ScaleAnimationRoute>
    with SingleTickerProviderStateMixin{
  Animation<double> animation;
  AnimationController controller;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    controller = new AnimationController(
        duration: const Duration(seconds: 3), vsync: this);
    //使用弹性曲线
    animation = CurvedAnimation(parent: controller, curve: Curves.bounceIn);
    //图片宽高从0变到300
    animation = new Tween(begin: 0.0, end: 300.0).animate(controller)
      ..addListener(() {
        setState(()=>{});
      });
    //启动动画(正向执行)
    controller.forward();
  }

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Center(
      child: Image.asset("graphics/HK.png",
        width: animation.value,height: animation.value,),
    );
  }

  @override
  void dispose() {
    // TODO: implement dispose

    //路由销毁时需要释放动画资源
    controller.dispose();
    super.dispose();
  }

}

//使用AnimatedWidget简化

/*
* 使用AnimatedWidget简化
*
* 细心的读者可能已经发现上面示例中通过addListener()和setState() 来更新UI这一步其实是通用的，
* 如果每个动画中都加这么一句是比较繁琐的。AnimatedWidget类封装了调用setState()的细节，
* 并允许我们将widget分离出来，重构后的代码如下：
* */

class AnimatedImage extends AnimatedWidget{
  AnimatedImage({Key key,Animation<double>animation})
      :super(key: key,listenable:animation);

  @override
  Widget build(BuildContext context) {
    final Animation<double> animation = listenable;
    // TODO: implement build
    return Center(
      child: Image.asset("graphics/HK.png",
        width: animation.value,height: animation.value,),
    );
  }
}

class ScaleAnimationRoute1 extends StatefulWidget{
  @override
  _ScaleAnimationRouteState1 createState() => _ScaleAnimationRouteState1();
}

class _ScaleAnimationRouteState1 extends State<ScaleAnimationRoute1>
    with SingleTickerProviderStateMixin{
  Animation<double> animation;
  AnimationController controller;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    controller = AnimationController(duration: Duration(seconds: 3),vsync: this);
    //图片宽高从0变到300
    animation = Tween(begin: 0.0,end: 300.0).animate(controller);
    animation.addStatusListener((status){
      if(status == AnimationStatus.completed){
        controller.reverse();
      }else if(status == AnimationStatus.dismissed){
        controller.forward();
      }
    });
    //启动动画
    controller.forward();
  }

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return AnimatedImage(animation: animation,);
  }

  @override
  void dispose() {
    // TODO: implement dispose
    controller.dispose();
    super.dispose();
  }
}

//用AnimatedBuilder重构
/*
* 用AnimatedBuilder重构
*
* 用AnimatedWidget可以从动画中分离出widget，而动画的渲染过程（即设置宽高）
* 仍然在AnimatedWidget中，假设如果我们再添加一个widget透明度变化的动画，
* 那么我们需要再实现一个AnimatedWidget，这样不是很优雅，如果我们能把渲染过程也抽象出来，
* 那就会好很多，而AnimatedBuilder正是将渲染逻辑分离出来, 上面的build方法中的代码可以改为：
* */

class ScaleAnimationRoute2 extends StatefulWidget{
  @override
  _ScaleAnimationRouteState2 createState()=>_ScaleAnimationRouteState2();
}

class _ScaleAnimationRouteState2 extends State<ScaleAnimationRoute2>
    with SingleTickerProviderStateMixin{
  Animation<double> animation;

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return AnimatedBuilder(
        animation: animation,child: Image.asset("graphics/HK.png"),
        builder: (BuildContext context,Widget child){
          return Center(child: Container(
            child: child,
            height: animation.value,
            width: animation.value,
          ),);
        });
  }
}

/*
* 上面的代码中有一个迷惑的问题是，child看起来像被指定了两次。但实际发生的事情是：
* 将外部引用child传递给AnimatedBuilder后AnimatedBuilder再将其传递给匿名构造器，
* 然后将该对象用作其子对象。最终的结果是AnimatedBuilder返回的对象插入到widget树中。

也许你会说这和我们刚开始的示例差不了多少，其实它会带来三个好处：

不用显式的去添加帧监听器，然后再调用setState() 了，这个好处和AnimatedWidget是一样的。

动画构建的范围缩小了，如果没有builder，setState()将会在父组件上下文中调用，
这将会导致父组件的build方法重新调用；而有了builder之后，只会导致动画widget自身的build重新调用，
避免不必要的rebuild。

通过AnimatedBuilder可以封装常见的过渡效果来复用动画。下面我们通过封装一个GrowTransition来说明，
它可以对子widget实现放大动画：
*/

class GrowTransition extends StatelessWidget{
  GrowTransition({this.child, this.animation});

  final Widget child;
  final Animation<double>animation;

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Center(
      child: AnimatedBuilder(animation: animation,
        builder: (BuildContext ctx,Widget child){
          return Container(
            height: animation.value,
            width: animation.value,
            child: child,
          );
        },
        child: child,
      ),
    );
  }
}

/*
* 这样，最初的示例就可以改为：

...
Widget build(BuildContext context) {
    return GrowTransition(
    child: Image.asset("images/avatar.png"),
    animation: animation,
    );
}
Flutter中正是通过这种方式封装了很多动画，
如：FadeTransition、ScaleTransition、SizeTransition、FractionalTranslation等，
很多时候都可以复用这些预置的过渡类。

枚举值	含义
dismissed	动画在起始点停止
forward	动画正在正向执行
reverse	动画正在反向执行
completed	动画在终点停止

*/

